/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlForeignKey;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.RedundantCol;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;

/**
 * User interface for the <i>redundant column removal pattern</i>
 * 
 * @author Tudor Dobrila
 * 
 */
public class RedundantColDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 8839688472380799011L;

	private final static Logger LOGGER = Logger
			.getLogger("com.dta.idbi.patterns.ui.RedundantColDialog");

	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length != 2) {
			return false;
		}

		if (!(objects[0] instanceof ISqlColumn) || !(objects[1] instanceof ISqlColumn)) {
			return false;
		}

		ISqlColumn col1 = (ISqlColumn) objects[0];
		ISqlColumn col2 = (ISqlColumn) objects[1];

		// Columns must have the same name
		if (!col1.getValue().equals(col2.getValue())) {
			return false;
		}

		ISqlTable table1 = (ISqlTable) col1.getParent();
		ISqlTable table2 = (ISqlTable) col2.getParent();
		List<String> supertypes1 = table1.getSupertypesNames();
		List<String> supertypes2 = table2.getSupertypesNames();
		if (!supertypes1.contains(table2.getValue()) && !supertypes2.contains(table1.getValue())) {
			// Parent tables are unrelated
			return false;
		}

		return true;
	}

	/**
	 * Parent table
	 */
	private ISqlTable parentTbl;

	/**
	 * Child table
	 */
	private ISqlTable childTbl;

	/**
	 * Reverse normalisation pattern dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public RedundantColDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.AbstractPatternDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		initialise();
		ISqlTable[] tables = elementsToGraph(graph);
		try {
			ISqlColumn col = tables[0].getColumn(element[0].getValue().toString());
			col.setStyle("SEL_COL");
			col = tables[1].getColumn(element[1].getValue().toString());
			col.setStyle("SEL_COL");
		} finally {
			graph.refresh();
		}
		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.AbstractPatternDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new SqlGraph();
		ISqlTable[] tables = elementsToGraph(graph);
		graph.getModel().beginUpdate();
		try {
			ISqlColumn col = tables[1].getColumn(element[1].getValue().toString());
			tables[1].removeColumn(col);
			tables[1].relayout(graph);
			graph.getModel().remove(col);
		} finally {
			graph.getModel().endUpdate();
			graph.refresh();
		}
		return new mxGraphComponent(graph);
	}

	/**
	 * Initialise the pattern
	 */
	private void initialise() {
		ISqlColumn col1 = (ISqlColumn) element[0];
		ISqlColumn col2 = (ISqlColumn) element[1];
		ISqlTable table1 = (ISqlTable) col1.getParent();
		ISqlTable table2 = (ISqlTable) col2.getParent();
		List<String> supertypes1 = table1.getSupertypesNames();
		List<String> supertypes2 = table2.getSupertypesNames();
		if (supertypes1.contains(table2.getValue())) {
			parentTbl = table2;
			childTbl = table1;
		} else if (supertypes2.contains(table1.getValue())) {
			parentTbl = table1;
			childTbl = table2;
		}

		macroField.setText(RedundantCol.COMMAND + " (<<" + parentTbl.getValue() + ","
				+ col1.getValue() + ">>, <<" + childTbl.getValue() + "," + col1.getValue() + ">>)");
	}

	/**
	 * Draw the parent and childs table to a graph
	 * 
	 * @param graph
	 *            Graph where to draw the parent and child tables
	 * @return Clones of the parent and child tables
	 */
	private ISqlTable[] elementsToGraph(SqlGraph graph) {
		ISqlTable[] result = new ISqlTable[2];
		graph.getModel().beginUpdate();
		try {
			final SqlTableCell newTable1 = (SqlTableCell) parentTbl.clone();
			mxGeometry newGeometry = new mxGeometry(5, 5, parentTbl.getGeometry().getWidth(),
					parentTbl.getGeometry().getHeight());
			newTable1.setGeometry(newGeometry);
			newTable1.relayout(graph);
			result[0] = newTable1;

			final SqlTableCell newTable2 = (SqlTableCell) childTbl.clone();
			newGeometry = new mxGeometry(200, 5, childTbl.getGeometry().getWidth(), childTbl
					.getGeometry().getHeight());
			newTable2.setGeometry(newGeometry);
			newTable2.relayout(graph);
			result[1] = newTable2;

			ISqlForeignKey fk = new SqlForeignKeyCell(null, null, "", "", newTable2, newTable1,
					"FK_EDGE");
			graph.addCell(fk, graph.getDefaultParent());
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal error", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return result;
	}

}
